/**
 * @file PAT.c
 *
 * @author Luc Fourestier
 * @date 2009
 */

#include "PAT.h"

#include "Global.h"

#define CRC_SIZE 4
#define MIN_PAT_SIZE 12+CRC_SIZE


/**
 * Create the PAT_t structure
 * Be careful! It does not check if the buffer is a PAT.
 *
 * @param Buffer (in): The incoming buffer containing the PAT
 * @param Size (in/out): The max buffer size and the actual parsed size as output
 * @param PAT (out): The PAT output pointer (must be allocated by caller)
 * @return errors
 */
int PAT_Create(unsigned char * Buffer, unsigned int *Size, PAT_t * PAT)
{
#define step(n) Index += n

	int RetVal = OK;
	unsigned int Index = 0;

	if((PAT != NULL) && (Buffer != NULL) && (Size != NULL) && (*Size >= MIN_PAT_SIZE))
	{
		unsigned int Temp;

		PAT->table_id = GET_U8(Buffer, Index);
		step(1);
		PAT->section_syntax_indicator = (GET_U8(Buffer, Index) & 0x80) >> 7;
		PAT->zero = (GET_U8(Buffer, Index) & 0x40) >> 6;
		PAT->reserved_1 = (GET_U8(Buffer, Index) & 0x30) >> 4;
		PAT->section_length = GET_U16(Buffer, Index) & 0xfff;
		step(2);
		PAT->transport_stream_id = GET_U16(Buffer, Index);
		step(2);
		PAT->reserved_2 = (GET_U8(Buffer, Index) & 0xc0) >> 6;
		PAT->version_number = (GET_U8(Buffer, Index) & 0x3e) >> 1;
		PAT->current_next_indicator = GET_U8(Buffer, Index) & 0x1;
		step(1);
		PAT->section_number = GET_U8(Buffer, Index);
		step(1);
		PAT->last_section_number = GET_U8(Buffer, Index);
		step(1);

		PAT->ProgramDescNumber = 0;

		Temp = 3 + PAT->section_length - CRC_SIZE;
		while((Index < (*Size-CRC_SIZE)) && (Index < Temp))
		{
			if (PAT->ProgramDescNumber < PAT_MAX_PROGRAM_DESC) // Because of table limitation
			{
				PAT->program_descriptors[PAT->ProgramDescNumber].program_number = GET_U16(Buffer, Index);
				step(2);
				PAT->program_descriptors[PAT->ProgramDescNumber].PID = GET_U16(Buffer, Index) & 0x13ff;
				step(2);
				PAT->ProgramDescNumber++;
			}
			else
			{
				// Discard the rest
				step(4);
			}
		}

		PAT->CRC_32 = GET_U32(Buffer, Index);
		step(4);

		*Size = Index;
	}
	else
	{
		RetVal = ERROR;
	}

	return(RetVal);

#undef step
}

/**
 * Print the PAT in details.
 *
 * @param PAT (in(: The PAT to print
 * @param Name (in): The name of the PAT (any nickname you wish! Can be NULL).
 * @param level (in): Level of details (Lowest = 0).
 * @param Output (in): Where to print (stdout or FILE).
 * @return Errors
 */
int PAT_Print(PAT_t * PAT, char * Name, int level, FILE * Output)
{
	int RetVal = OK;
	int i = 0;
	int Min = 0;

	if ((PAT == NULL) && (Output == NULL))
	{
		return(ERROR);
	}

	if (Name == NULL)
	{
		Name = "PAT";
	}

	fprintf(Output, "%1$s table_id: %2$i (0x%2$x)\n", Name, (int)PAT->table_id);
	fprintf(Output, "%1$s section_syntax_indicator: %2$i (0x%2$x)\n", Name, (int)PAT->section_syntax_indicator);
	fprintf(Output, "%1$s section_length: %2$i (0x%2$x)\n", Name, (int)PAT->section_length);
	fprintf(Output, "%1$s transport_stream_id: %2$i (0x%2$x)\n", Name, (int)PAT->transport_stream_id);
	fprintf(Output, "%1$s version_number: %2$i (0x%2$x)\n", Name, (int)PAT->version_number);
	fprintf(Output, "%1$s current_next_indicator: %2$i (0x%2$x)\n", Name, (int)PAT->current_next_indicator);
	fprintf(Output, "%1$s section_number: %2$i (0x%2$x)\n", Name, (int)PAT->section_number);
	fprintf(Output, "%1$s last_section_number: %2$i (0x%2$x)\n", Name, (int)PAT->last_section_number);
	fprintf(Output, "%1$s ProgramDescNumber: %2$i (0x%2$x)\n", Name, (int)PAT->ProgramDescNumber);

	Min = PAT->ProgramDescNumber > PAT_MAX_PROGRAM_DESC ? PAT_MAX_PROGRAM_DESC : PAT->ProgramDescNumber;
	for (i=0; i<Min; i++)
	{
		fprintf(Output, "%s program_descriptors[%i]\n", Name, i);
		fprintf(Output, "\t program_number: %1$i (0x%1$x)\n", (int)PAT->program_descriptors[i].program_number);
		fprintf(Output, "\t PID: %1$i (0x%1$x)\n", (int)PAT->program_descriptors[i].PID);
	}

	fprintf(Output, "\n");

	return(RetVal);
}

/**
 * Check if the buffer is really a PAT
 *
 * @param Buffer (in): The input buffer
 * @param Size (in): The buffer size
 * @return OK if contains PAT, else PAT_ERROR_NOT_A_PAT
 */
int PAT_CheckPAT(unsigned char * Buffer, unsigned int Size)
{
	int RetVal = OK;
	unsigned char table_id = 0xff;

	if((Buffer != NULL) && (Size >= MIN_PAT_SIZE))
	{
		table_id = GET_U8(Buffer, 0);
		if (table_id != PAT_TID)
		{
			RetVal = PAT_ERROR_NOT_A_PAT;
		}
	}
	else
	{
		RetVal = ERROR;
	}

	return(RetVal);
}

/**
 * Return the TSID field of the PAT (faster than creating the PAT_t).
 * Be careful! It does not check if the buffer is a PAT.
 *
 * @param Buffer (in): The input buffer
 * @param Size (in): The buffer size
 * @param TSID (out): The TSID
 * @return errors
 */
int PAT_GetTSID(unsigned char * Buffer, unsigned int Size, unsigned short *TSID)
{
	int RetVal = OK;

	if((Buffer != NULL) && (Size >= MIN_PAT_SIZE) && (TSID != NULL))
	{
		*TSID = GET_U16(Buffer, 3);
	}
	else
	{
		RetVal = ERROR;
	}

	return(RetVal);
}


